import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by Kozlovskyi.
 * CircularBuffer with ReentrantLock and Condition without library java.until.concurrent
 */
public class CircularBufferConcurrent implements Buffer<Integer> {
    private Integer[] buffer;
    private int read;
    private int write;
    private final Lock lock;
    private final Condition notFull;
    private final Condition notEmpty;
    //------------------------------------------------------------
    public CircularBufferConcurrent(int capacity) {
        buffer = new Integer[capacity];
        lock = new ReentrantLock();
        notFull = lock.newCondition();
        notEmpty = lock.newCondition();
        read = 0;
        write = -1;
    }
    //------------------------------------------------------------
    @Override
    public void put(Integer item) {
        lock.lock();
        try {
            while (isFull()) {
                notFull.await();
            }
            int pos = (write + 1) % buffer.length;
            buffer[pos] = item;
            write = pos;
            System.out.println(item + " added to buffer");
            notEmpty.signalAll();
        } catch (InterruptedException ex) {
            System.out.println("Interrupted  Exception when putting" + Thread.currentThread()
                    + item);
        } finally {
            lock.unlock();
        }
    }
    //------------------------------------------------------------
    @Override
    public Integer get() {
        lock.lock();
        try {
            while (isEmpty()) {
                notEmpty.await();
            }
            Integer item = buffer[read];
            buffer[read] = null;
            read = (1 + read) % buffer.length;
            notFull.signalAll();
            return item;
        } catch (InterruptedException ex) {
            System.out.println("Interrupted  Exception when getting" + Thread.currentThread());
            return null;
        } finally {
            lock.unlock();
        }
    }
    //------------------------------------------------------------
    @Override
    public boolean isEmpty() {
        for (int i = 0; i < buffer.length; ++i) {
            if (buffer[i] != null) {
                return false;
            }
        }
        return true;
    }
    //------------------------------------------------------------
    @Override
    public boolean isFull() {
        for (int i = 0; i < buffer.length; ++i) {
            if (buffer[i] == null) {
                return false;
            }
        }
        return true;
    }
    //------------------------------------------------------------
}
